package org.sgs.reddit.bot;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.sgs.reddit.bot.config.RedditBotConstants;
import org.sgs.reddit.bot.json.RedditJsonConverter;
import org.sgs.reddit.bot.json.RedditLogin;
import org.sgs.reddit.bot.json.RedditMe;
import org.sgs.reddit.bot.log.LogUtils;


public class RedditClient {

  // The api says do not make more that one request per 2 seconds
  private static final int REQUEST_DELAY = 2;

  private DefaultHttpClient httpClient;

  private long currNetworkDecay;


  public RedditClient() {
    httpClient = new DefaultHttpClient();
    setRedditCookie(RedditBotConstants.BF_BOT_TESTER_COOKIE);
    currNetworkDecay = 2;
    if (!loggedIn()) {
      updateCookie();
    }
  }


  /*
   * Method that attempts login, tests the cookie, and persists cookie in memory and disk once successful. Utilize network decay so as not to spam the login api.
   */
  private void updateCookie() {

    while (true) {

      // Sleep between each login attempt
      LogUtils.debug("Sleeping %d seconds before login attempt.", currNetworkDecay);
      sleep(currNetworkDecay);

      // Make login attempt
      RedditLogin redditLogin = null;
      try {
        redditLogin = getRedditLogin();
      } catch (Throwable t) {
        LogUtils.debug("Login attempt failed: '%s'", t.getMessage());
        currNetworkDecay = (currNetworkDecay == 0 ? REQUEST_DELAY : currNetworkDecay * 2);
        continue;
      }

      // We have a succsessful login, set cookie in client and persist in config
      setRedditCookie(redditLogin.getCookieString());

      // Obey api request time limit
      sleep(REQUEST_DELAY);

      // Test that cookie is good
      if (!loggedIn()) {
        // Cookie is no good, or server is having issues
        currNetworkDecay = (currNetworkDecay == 0 ? REQUEST_DELAY : currNetworkDecay * 2);
        continue;
      }

      // If we arrive here, we have a good cookie
      currNetworkDecay = 0;
      break;

    }// while

  }// updateCookie


  private void sleep(long secondsToSleep) {

    if (secondsToSleep == 0) {
      return;
    }

    try {
      Thread.sleep(secondsToSleep * 1000);
    } catch (InterruptedException e) {
      LogUtils.warn("Sleep interupted!");
    }

  }


  private boolean loggedIn() {

    RedditMe redditMe = null;
    try {
      redditMe = getRedditMe();
    } catch (Throwable t) {
      LogUtils.info("Not logged in: '%s'", t.getMessage());
      return false;
    }

    if (redditMe.getKind() == null || redditMe.getData() == null) {
      LogUtils.info("Not logged in, RedditMe response empty.");
      return false;
    }

    return true;
  }


  private void setRedditCookie(String cookieString) {
    CookieStore cookieStore = new BasicCookieStore();
    BasicClientCookie cookie = new BasicClientCookie("reddit_session", cookieString);
    cookie.setDomain("reddit.com");
    cookie.setPath("/");
    cookieStore.addCookie(cookie);
    httpClient.setCookieStore(cookieStore);
    persistCookieInConfig(cookieString);
  }


  private RedditLogin getRedditLogin() {

    // Set up for login with POST of user/pass
    HttpPost httpPost = new HttpPost("http://www.reddit.com/api/login/" + RedditBotConstants.BF_BOT_TESTER_USERNAME);
    List<NameValuePair> nameValuePairs = new LinkedList<NameValuePair>();
    nameValuePairs.add(new BasicNameValuePair("user", RedditBotConstants.BF_BOT_TESTER_USERNAME));
    nameValuePairs.add(new BasicNameValuePair("passwd", RedditBotConstants.BF_BOT_TESTER_PASSWORD));
    nameValuePairs.add(new BasicNameValuePair("api_type", "json")); // Need this for mod_hash

    try {
      httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }

    // Do login
    HttpResponse response = null;
    try {
      response = httpClient.execute(httpPost);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    HttpEntity httpEntity = response.getEntity();
    InputStream jsonStream = null;
    try {
      jsonStream = httpEntity.getContent();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    return RedditJsonConverter.getRedditLogin(jsonStream);
  }


  private RedditMe getRedditMe() {

    HttpGet httpGet = new HttpGet(RedditBotConstants.REDDIT_URL + RedditBotConstants.API_REDDIT_ME);

    HttpResponse response = null;
    try {
      response = httpClient.execute(httpGet);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    if (response.getStatusLine().getStatusCode() > 399) {
      throw new RuntimeException("Returned with bad status code: " + response.getStatusLine().getStatusCode());
    }

    HttpEntity httpEntity = response.getEntity();

    RedditMe redditMe = null;
    try {
      redditMe = RedditJsonConverter.getRedditMe(httpEntity.getContent());
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    return redditMe;
  }


  private void persistCookieInConfig(String cookieString) {
    // TODO: implement this
  }

}
